说说同源策略和跨域
同源策略是什么?
如果两个 URL 的协议、端口和域名都完全一致的话,则这两个 URL 是同源的。
http://www.baidu.com/s
http://www.baidu.com:80/ssdasdsadad
同源策略怎么做?
只要在浏览器里打开页面,就默认遵守同源策略。
优点
保证用户的隐私安全和数据安全。
缺点
很多时候,前端需要访问另一个域名的后端接口,会被浏览器阻止其获取响应。
比如甲站点通过 AJAX 访问乙站点的 /money 查询余额接口,请求会发出,但是响应会被浏览器屏蔽。
怎么解决缺点
跨域就是突破浏览器同源策略限制,实现数据传递。
跨域的方法
CORS
JOSNP
使用跨域手段。
JSONP(前端体系课有完整且详细的介绍)
a.甲站点利用 script 标签可以跨域的特性,向乙站点发送 get 请求。
b.乙站点后端改造 JS 文件的内容,将数据传进回调函数。
c.甲站点通过回调函数拿到乙站点的数据。
CORS(前端体系课有完整且详细的介绍)
a.对于简单请求,乙站点在响应头里添加 Access-Control-Allow-Origin: http://甲站点 即可。
b.对于复杂请求,如 PATCH,乙站点需要:
i.响应 OPTIONS 请求,在响应中添加如下的响应头:
Access-Control-Allow-Origin: https://甲站点
Access-Control-Allow-Methods: POST, GET, OPTIONS, PATCH
Access-Control-Allow-Headers: Content-Type
ii.响应 POST 请求,在响应中添加 Access-Control-Allow-Origin 头。c.如果需要附带身份信息,JS 中需要在 AJAX 里设置 xhr.withCredentials = true 。
Nginx 代理 / Node.js 代理
前端 ⇒ 后端 ⇒ 另一个域名的后端
详情参考 MDN CORS 文档。
如何用跨域解决前端技术
跨域是什么
CORS
通知后端,后端只需在服务端的响应头里添加一句,允许特定网址访问即可
if (path === '/friends.json') {
response.statusCode = 200
response.setHeader('Content-Type', 'text/json;charset=utf-8')
//新增:允许特定网址(http://localhost:9999)访问 或者 *
response.setHeader('Access-Control-Allow-Origin', 'http://localhost:9999')
response.write(fs.readFileSync('./friends.json'))
response.end()
}
参考文章:https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Access_control_CORS
JSONP
是什么
JSONP 是通过 script 标签加载数据的方式去获取 JSON 数据当做 JS 代码来执行。
简单来说,通过 script 标签请求另一个网站 JS 文件,JS 文件会执行一个回调,回调中返回后会夹带服务器的数据
详细说明:客户端请求一个 JS 文件,JS 文件提前在页面上声明一个回调函数,回调函数名随机生成的,当发送服务器请求时,函数名通过 callback 接口传参的方式传给后台服务器,当服务器解析到参数(函数名)后,原始数据会上「包裹」这个函数名(服务端把 JSON 数据加载到函数里)之后发送给前端。前端就可以拿到服务端数据
为什么用 JSONP
当我们在进行跨域时,如果当前浏览器不支持 CORS,或者因为某些条件不支持 CORS,则会使用 JSONP 进行跨域
JSONP 优点
- 兼容 IE
- 可以跨域
JSONP 缺点
- 由于 JSONP 是 script 标签,无法像 AJAX 读取状态码,只知道成功和失败
- 由于 JSONP 是 script 标签,他只能发 get 请求,不支持 post 请求
- JSONP 需要对应接口的后端的配合才能实现。
只能用 get 请求,只支持跨域 HTTP 请求这种情况,不能解决不同域的两个页面之间如何进行 JavaScript 调用的问题 JSONP 的优势在于支持老式浏览器,弊端也比较明显:需要客户端和服务端定制进行开发,服务端返回的数据不能是标准的 Json 数据,而是 callback 包裹的数据。
示例
客户端 frank.js
思路:
- 声明 random 随机 callback
- 创建 script 标签
- 全局中获取数据
- 删除 script 标签
function jsonp(url) {
return new Promise((resolve, reject) => {
const random = 'frnkJSONPCallbackName' + Math.random()
const script = document.createElement('script')
script.src = `${url}?callback=${random}`
document.body.appendChild(script)
window[random] = data => {
resolve(data)
}
script.error = () => {
reject()
}
script.onload = () => {
script.remove()
}
})
}
jsonp('http://localhost:8888/friends.js').then(res => console.log(res))
服务端 server.js
思路:
- 查看 referer 来源
- 设置相应头信息
- 数据库查找数据后 写入相应体 挂载到全局
- 返回相应
if (path === '/friends.js') {
console.log(request.headers['referer'])
if (request.headers['referer'].indexOf('http://localhost:9999/index.html') === 0) {
response.statusCode = 200
console.log(query.callback)
response.setHeader('Content-Type', 'text/javascript;charset=utf-8')
const string = `window['{{xxx}}']( {{data}} )`
const data = fs.readFileSync('./friends.json').toString()
const string2 = string.replace('{{data}}', data).replace('{{xxx}}', query.callback)
response.write(string2)
response.end()
} else {
response.statusCode = 404
response.end()
}
}